﻿using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System.Linq;
using System.Globalization;

namespace CWM.Skinn
{
    public static partial class SkinnContextCommands
    {
        [CtxMenuItem("SkinnedMeshRenderer/Shapes/Create/Expanded Shape",
            "Creates a blend-shape by expanding the mesh three inches.", 0)]
        public static void SkinnedMeshRendererShapeCreateExpandAll05()
        {
            SkinnedMeshRendererShapeCreateExpandAll(3f.ToUnitFromStandard(ConversionUnits.Inch));
        }

        [CtxMenuItem("SkinnedMeshRenderer/Shapes/Create/Bones/Expanded Shape All",
           "Creates a blend-shapes by expanding the mesh three inches for each bone.", 0)]
        public static void SkinnedMeshRendererShapeCreateExpandAllBones()
        {

            foreach (var item in SkinnContext.GetContextItems())
            {
                var context = item.GetComponent<SkinnedMeshRenderer>();
                if (SkinnEx.IsNullOrNotInAScene(context)) continue;
                if (!SkinnEx.HasMinimumRequirements(context)) continue;
                SkinnContext.UndoRecord(context, "Create Expand All");
                SkinnedMeshRendererShapeCreateExpandAllBones(context, 3f.ToUnitFromStandard(ConversionUnits.Inch));
            }

            SkinnContext.Finished();

        }

        public static void SkinnedMeshRendererShapeCreateExpandAll(float size)
        {
            foreach (var item in SkinnContext.GetContextItems())
            {
                var context = item.GetComponent<SkinnedMeshRenderer>();
                if (SkinnEx.IsNullOrNotInAScene(context)) continue;
                var mesh = context.GetSharedMesh().Clone() as Mesh;

                mesh.ClearBlendShapes();
                Vector3[] vertices, normals, tangents;
                if (!SkinnEx.Inflate(mesh, out vertices, out normals, out tangents, size, true)) continue;

                mesh.AddBlendShapeFrame("Expand All 0.5", 100, vertices, normals, tangents);

                mesh.CopyBlendshapes(context.GetSharedMesh());

                SkinnContext.UndoRecord(context, "Create Expand All");
                context.sharedMesh = mesh;
            }

            SkinnContext.Finished();
        }


        public static void SkinnedMeshRendererShapeCreateExpandAllBones(SkinnedMeshRenderer source,  float size)
        {
            if (!SkinnEx.HasMinimumRequirements(source)) return;

            var mesh = source.GetSharedMesh().Clone() as Mesh;
            mesh.ClearBlendShapes();

            Vector3[] vertices, normals, tangents;
            if (!SkinnEx.Inflate(mesh, out vertices, out normals, out tangents, size, true)) { return; }

            var bones = source.bones.ToList();
            var usedBones = new List<int>(bones.Count);
            var bonesSets = new Dictionary<string, int[]>(bones.Count);

            for (int i = 0; i < bones.Count; i++)
            {
                if (usedBones.Contains(i)) continue;

                var bone = bones[i]; if (!bone) continue;
                string commonName;
                var mirrorBone = bones[i].FindMirrorByName(true, bones, out commonName);
                int[] boneMask;
                int mirrorIndex = mirrorBone ? bones.FindIndex((x) => { return x == mirrorBone; }) : -1;
                if (mirrorIndex > -1)
                {
                    usedBones.Add(mirrorIndex);
                    boneMask = new int[] { i, mirrorIndex };
                }
                else
                {
                    commonName = bone.name.ToLower();
                    boneMask = new int[] { i };
                }

                usedBones.Add(i);
                var textInfo = new CultureInfo("en-US", false).TextInfo;
                commonName = textInfo.ToTitleCase(commonName);

                if (!bonesSets.ContainsKey(commonName)) bonesSets.Add(commonName, boneMask);
            }

            var boneWeights = source.GetSharedMesh().boneWeights;

            foreach (var blendshape in bonesSets)
            {
                Vector3[] deltaVertices, deltaNormals, deltaTangets;

                var mask = blendshape.Value;

                if (!vertices.Mask(boneWeights, mask, out deltaVertices, 1f)){ break; }
                if (!normals.Mask(boneWeights, mask, out deltaNormals, 1f)) { break; }
                if (!tangents.Mask(boneWeights, mask, out deltaTangets, 1f)) { break; }

                mesh.AddBlendShapeFrame(blendshape.Key, 100, deltaVertices, deltaNormals, deltaTangets);
            }

            mesh.CopyBlendshapes(source.GetSharedMesh());
            source.sharedMesh = mesh;
        }
    }
}